/*********************************************************************************
 * Software License Agreement (BSD License)
 * Copyright (c) 2012, Carlos D. Correa (info@ngraph.org)
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * 
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of netzen nor the
 *    names of its contributors may be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *********************************************************************************/
#include "sensitivityView.h"
#include "propertyUtils.h"
///*****************************
// Flow-based scatterplots
// version updated: Nov 7, 2011
// changes:
//		- Reverted to simpler version
//	  - New flow-based moved to SensitivityPlotBeta
//*****************************/

SensitivityPlot::SensitivityPlot(const char *title): ScatterPlot(title, "node"){//: ScatterPlot(title) {
	lines = 0;
	invalidateStreamlines = true;
	hoveredNode = 0;	
}
SensitivityPlot::~SensitivityPlot(){

}

void SensitivityPlot::setup() {
	ScatterPlot::setup();
	previousSelected = 0;
	setPropertyStringList("Data.DX", "Default", "node");
	setPropertyStringList("Data.DY", "Default", "node");
	setPropertyStringList("Data.Properties", "Default", "node", true);
	setPropertyFloat("Trend.Neighborhood", 1.0);
	setPropertyInt("Trend.Line Width", 1);
	setPropertyFloat("Trend.Transparency", 1.0);
	setPropertyFloat("Trend.Length", 0.04);
	setPropertyBool("Streamlines.Show", false);
	setPropertyBool("Streamlines.Cluster", false);
	setPropertyInt("Streamlines.Number Clusters", 10);
	setPropertyInt("Trend.Method", 0);
	clustered = false;
	setMargins(80, 40, 40, 60);
	curPropertyDerivX = "";
	curPropertyDerivY = "";
	
	invalidateStreamlines = true;
	hoveredNode = 0;
}

void SensitivityPlot::createLines(int n) {
	if(n!=numVertices)	{
		numVertices = n;
		if(lines) delete lines;
		lines = new float[4*n];
	}
}

string SensitivityPlot::getPropertyDX(){
    return PROPERTY(getPropertyString("Data.DX"));
}
string SensitivityPlot::getPropertyDY(){
    return PROPERTY(getPropertyString("Data.DY"));
}

void SensitivityPlot::updateLines() {
	invalidateStreamlines = true;
	string propertyX = getPropertyX();
	string propertyY = getPropertyY();
	string dX = PROPERTY(getPropertyString("Data.DX"));
	string dY = PROPERTY(getPropertyString("Data.DY"));
	float length = getPropertyFloat("Trend.Length");
	
	int i = 0;
	ElementIterator *it = createIterator();
	for(it->begin();it->hasMoreElements();) {
		PropertyBag *ndj = it->next();
		assert(ndj);
		float vx, vy;
		//sampleDerivative(x, y, vx, vy);
		sampleDerivativeND((Node*) ndj, vx, vy);
		
		float len  = sqrt(vx*vx+vy*vy);
		if(len) {
			vx/=len;
			vy/=len;
		}
		float xn, yn;
		xn = getX(ndj, propertyX);
		yn = getY(ndj, propertyY);
		lines[4*i+0] = transformX(propertyX,xn-vx*length);
		lines[4*i+1] = transformY(propertyY,yn-vy*length);
		lines[4*i+2] = transformX(propertyX,xn+vx*length);
		lines[4*i+3] = transformY(propertyY,yn+vy*length);
		i++;
	}
}


void SensitivityPlot::renderLines() {
	glColor4f(0,0,0,1);
	glLineWidth(getPropertyInt("Trend.Line Width"));
	/*
	 glVertexPointer(2, GL_FLOAT, 0, lines);
	 glColorPointer(4, GL_FLOAT, 0, colors);
	 glEnableClientState(GL_VERTEX_ARRAY);
	 glEnableClientState(GL_COLOR_ARRAY);
	 //glDisableClientState(GL_COLOR_ARRAY);
	 glDrawArrays(GL_LINES, 0, 2*numVertices);
	 */
	
	glColor4f(0,0,0,1);
	for(int i = 0; i < numVertices;i++) {
		//printf("Drawing Lines[%d] =  %f %f %f %f\n", i, lines[4*i+0], lines[4*i+1], lines[4*i+2], lines[4*i+3]);
		glLineWidth(getPropertyInt("Trend.Line Width"));
		glColor4fv(&(colors[4*i+0]));
		glBegin(GL_LINES); 
		glVertex2fv(&(lines[4*i+0]));
		glVertex2fv(&(lines[4*i+2]));
		glEnd();
		glLineWidth(1);
		glColor4f(0,0,0,0.4);
		glBegin(GL_LINES); 		  
		glVertex2fv(&(lines[4*i+0]));
		glVertex2fv(&(lines[4*i+2]));
		glEnd();
	}
	
	glLineWidth(1);
}

/* Carlos updated regression computation on 2010-1028:
   http://mathworld.wolfram.com/LeastSquaresFittingPerpendicularOffsets.html
  */
void SensitivityPlot::sampleDerivativeND(Node *node, float &vx, float  &vy) {
	string propertyX = PROPERTY(getPropertyString("Data.X"));
	string propertyY = PROPERTY(getPropertyString("Data.Y"));
	string dX = PROPERTY(getPropertyString("Data.DX"));
	string dY = PROPERTY(getPropertyString("Data.DY"));
	
	vector<string> properties = PropertyUtils::parseMultiProperty(getPropertyString("Data.Properties"));
	int N = properties.size();
	
	bool normalized = true;
	
	double *vn = new double[N];
	for(unsigned int k=0;k<properties.size();k++) {
		vn[k] = graph->getAsFloat(node, PROPERTY(properties[k]), 0, normalized);
	}
	double x = graph->getAsFloat(node, dX, 0, true);
	double y = graph->getAsFloat(node, dY, 0, true);
	double radius = getPropertyFloat("Trend.Neighborhood");
	
	int neighbors=0;
	double sumdx = 0;
	double sumdy = 0;
	double sumdxdx = 0, sumdydx = 0, sumdydy = 0;
	double sumw = 0;

	ElementIterator *it = createIterator();
	for(it->begin();it->hasMoreElements();) {
		PropertyBag *ndj = it->next();
		assert(ndj);
		double xj = graph->getAsFloat(ndj, dX, 0, true);
		double yj = graph->getAsFloat(ndj, dY, 0, true);
		double dis = 0;
		for(unsigned int k=0;k<properties.size();k++) {
			double vjn = graph->getAsFloat(ndj, PROPERTY(properties[k]), 0, normalized);
			dis+=(vjn-vn[k])*(vjn-vn[k]);
		}
		bool inRange = false;
		if(dis<radius*radius){
			inRange = true;
		} else {
			inRange = false;	
		}            
		if(inRange) {	 
			neighbors++;
			double omr = 1-sqrt(dis)/radius;
			double omrclamped = (omr<0)? 0: omr;
			double w = pow(omrclamped, 4)*(4*omr+1);  
			//w = exp(-5*5*dis/(radius*radius));
			double dx = xj-x; 
			double dy = yj-y; 
			sumdxdx+= w*dx*dx; 
			sumdydx+= w*dy*dx;
			sumdydy+= w*dy*dy;
			sumdx+=w*dx;
			sumdy+=w*dy;
			
			sumw +=w;
			
		}//endof if
	}//endof for
 graph->destroyIterator(it);
	
	double A, B, C;
	double B0 = (sumdx*sumdy/sumw - sumdydx);
	A = B0;
	B = sumdydy - sumdxdx + (sumdx*sumdx - sumdy*sumdy)/sumw;
	C = -B0;
	vx = -(-B + sqrt(B*B-4*A*C))/(2.0*A);
	vy = 1.0;
	
	if(getPropertyInt("Trend.Method")==2) {
		vx = sumdxdx/sumw;
		vy = sumdydy/sumw;
	}
	
	
	delete vn;
}

// Approximation of derivative using MLS
void SensitivityPlot::sampleDerivative(float x0, float y0, float &vx, float &vy) {
	string propertyX = getPropertyString("Data.X");
	string propertyY = getPropertyString("Data.Y");
	string dX = PROPERTY(getPropertyString("Data.DX"));
	string dY = PROPERTY(getPropertyString("Data.DY"));
	
	
	float x = x0;
	float y = y0;
	float xn = x0;
	float yn = y0;
	float radius = getPropertyFloat("Trend.Neighborhood");
	
	int neighbors=0;
	float sumdx = 0;
	float sumdy = 0;
	float sumdxdx = 0, sumdydx = 0, sumdydy = 0;
	float sumw = 0;
	ElementIterator *it = createIterator();
	for(it->begin();it->hasMoreElements();) {
		PropertyBag *ndj = it->next();
		assert(ndj);
		float xj = graph->getAsFloat(ndj, dX, 0, true);
		float yj = graph->getAsFloat(ndj, dY, 0, true);
		
		float xjn = xj;
		float yjn = yj;
		float dis = ((xn-xjn)*(xn-xjn)+(yn-yjn)*(yn-yjn));
		
		bool inRange = false;
		if(dis<radius*radius){
			inRange = true;
		} else {
			inRange = false;	
		}            
		if(inRange) {	 
			neighbors++;
			float omr = 1-sqrt(dis)/radius;
			float omrclamped = (omr<0)? 0: omr;
			float w = pow(omrclamped, 4)*(4*omr+1);  //
			//w = exp(-5*5*dis/(radius*radius));
			float dx = xj-x; 
			float dy = yj-y; 
			sumdxdx+= w*dx*dx; 
			sumdydx+= w*dy*dx; 
			sumdydy+= w*dy*dy; 
			sumdx+=w*dx;
			sumdy+=w*dy;
			
			sumw +=w;
		}//endof if
	}//endof for
 graph->destroyIterator(it);

	float A, B, C;
	A = sumdx*sumdy/sumw - sumdydx;
	B = sumdydy - sumdxdx + (sumdx*sumdx - sumdy*sumdy)/sumw;
	C = -A;
	vx = -(-B + sqrt(B*B-4*A*C))/(2.0*A);
	vy = 1.0;
	
	if(getPropertyInt("Trend.Method")==2) {
		vx = sumdxdx/sumw;
		vy = sumdydy/sumw;
	}
	
}
void SensitivityPlot::computeStreamline(int idx, float h, int N, bool backwards) {
	computeStreamline(graph->nodes[idx], h, N, backwards);
}

void SensitivityPlot::computeStreamline(Node *node, float h, int N, bool backwards) {
	assert(node);
	string propertyX = getPropertyString("Data.X");
	string propertyY = getPropertyString("Data.Y");
	string dX = PROPERTY(getPropertyString("Data.DX"));
	string dY = PROPERTY(getPropertyString("Data.DY"));
	
	streamline.clear();
	if(dX!=propertyX || dY!=propertyY) return;
	
	float minX, maxX, minY, maxY;
	minX = minY = 0.0;
	minY = maxY = 1.0;
	
	getMinMax(dX, minX, maxX);	
	getMinMax(dY, minY, maxY);	
	
	float vdx = graph->getAsFloat(node, dX, 0, true);
	float vdy = graph->getAsFloat(node, dY, 0, true);
	
	float derivx;
	float derivy;
	
	float posx = vdx;
	float posy = vdy;
	//print("deriv0 = %f %f\n", derivx, derivy);
	for(int sign = 1; sign>=-1;sign-=2) {
		posx = vdx;
		posy = vdy;
		if(!backwards && sign<0) break;
		float diffx = 0, diffy = 0;
		for(int i=0;i<N;i++) {
			pt p;
			p.x = transformX(propertyX,posx);
			p.y = transformY(propertyY,posy);
			//printf("Adding to streamline[%d] %f %f\n", i, p.x, p.y);
			if(p.x>1.0 || p.x<0.0 || p.y>1.0 || p.y<0.0) break;
			if(sign==-1) {
				streamline.insert(streamline.begin(), p);
			} else {
				streamline.push_back(p);
			}
			
			sampleDerivative(posx, posy, derivx, derivy);
			
			// Avoid going back if velocity is in the opposite direction.
			if(sign*(derivx*diffx + derivy*diffy)<0) {
				derivx*=-1.0;
				derivy*=-1.0;
			}
			
			float len  = sqrt(derivx*derivx + derivy*derivy);
			if(len) {
				derivx/=len;
				derivy/=len;
			}
			
			float halfx = posx + sign*0.5*h*derivx;
			float halfy = posy + sign*0.5*h*derivy;
			
			diffx = halfx - posx;
			diffy = halfy - posy;
			
			float ddx, ddy;
			sampleDerivative(halfx, halfy, ddx, ddy);
			
			// Avoid going back if velocity is in the opposite direction.
			if(sign*(ddx*diffx + ddy*diffy)<0) { 
				ddx*=-1.0;
				ddy*=-1.0;
			}
			
			len  = sqrt(ddx*ddx + ddy*ddy);
			if(len) {
				ddx/=len;
				ddy/=len;
			}
			
			posx = posx + sign*h*ddx;
			posy = posy + sign*h*ddy;
			
			diffx = sign*h*ddx;
			diffy = sign*h*ddy;
			
		}
	}
}
		
		
void SensitivityPlot::clusterPoints() {
	string propertyX = PROPERTY(getPropertyString("Data.X"));
	string propertyY = PROPERTY(getPropertyString("Data.Y"));
	
	double **data;
	int **mask;
	int nrows = (int) graph->nodes.size();
	int ncols = (int) 2;
	data = new pdouble[nrows];
	mask = new pint[nrows];
	for(int i=0;i<nrows;i++) {
		data[i] = new double[2];
		mask[i] = new int[2];
		data[i][0] = graph->getAsFloat(graph->nodes[i],propertyX,0, true);
		data[i][1] = graph->getAsFloat(graph->nodes[i],propertyY,0, true);
		mask[i][0] = 1;
		mask[i][1] = 1;
	}
	double *weight = new double[2];
	for(int p=0;p<2;p++) weight[p] = 1.0;
	
	double *error = new double[nrows];
	
	int *clusterid = new int[nrows];
	int ifound;
	
	print("Calling kcluster\n");
	int nclusters = getPropertyInt("Streamlines.Number Clusters");
	kcluster(nclusters, nrows, ncols, data, mask, weight, 0, 80, 'a', 'e', clusterid, error, &ifound);
	print("DONE\n");
	
	for(int k=0;k<nrows;k++) {
		int id = k;
		int cluster = clusterid[id];
		char str[20];
		sprintf(str, "%d", cluster); 
		graph->nodes[id]->setPropertyString("cluster", str);
	}
	controller->registerProperty("node", "cluster");
	
	delete clusterid;
	delete error;
	delete[] mask;
	delete[] data;
	delete weight;
}


void SensitivityPlot::computeStreamlines() {
	maxSize = 0;
	if(streamlines) delete[] streamlines;
	streamlines = new vector<pt>[graph->nodes.size()];
	for(unsigned int i = 0;i<graph->nodes.size();i++) {
		print(" %03d / %03d        \r", i, graph->nodes.size()); fflush(stdout);
		computeStreamline(i, 0.04, 40, true);
		streamlines[i].clear();
		maxSize = max(maxSize, (int) streamline.size());
		for(unsigned int k = 0;k<streamline.size();k++) {
			streamlines[i].push_back(streamline[k]);
		}
	}
}
void SensitivityPlot::clusterStreamlines() {
	if(!streamlines) computeStreamlines();
	double **data;
	int **mask;
	/*
	int nrows = (int) graph->nodes.size();
	int ncols = (int) 2*maxSize;
	data = new pdouble[nrows];
	mask = new pint[nrows];
	for(int i=0;i<nrows;i++) {
		data[i] = new double[2*maxSize];
		mask[i] = new int[2*maxSize];
		for(unsigned int k=0;k<streamlines[i].size();k++) {
			data[i][2*k+0] = streamlines[i][k].x;
			data[i][2*k+1] = streamlines[i][k].y;
			mask[i][2*k+0] = 1;
			mask[i][2*k+1] = 1;
		}
		int n = (int) streamlines[i].size();
		for(unsigned int k=streamlines[i].size(); k<(unsigned int) maxSize;k++) {
			data[i][2*k+0] = data[i][2*n+0];
			data[i][2*k+1] = data[i][2*n+1];
			mask[i][2*k+0] = mask[i][2*k+1] = 0;
		}
	}
	double *weight = new double[2*maxSize];
	for(int p=0;p<2*maxSize;p++) weight[p] = 1.0;
*/	

	 int nrows = (int) graph->nodes.size();
	 int ncols = (int) 4;
	 data = new pdouble[nrows];
	 mask = new pint[nrows];
	 for(int i=0;i<nrows;i++) {
		 data[i] = new double[ncols];
		 mask[i] = new int[ncols];
	 }

	 string propertyX = getPropertyX();
	 string propertyY = getPropertyY();
	 string dX = PROPERTY(getPropertyString("Data.DX"));
	 string dY = PROPERTY(getPropertyString("Data.DY"));
	 	 int i = 0;
	 ElementIterator *it = createIterator();
	 for(it->begin();it->hasMoreElements();) {
		 PropertyBag *ndj = it->next();
		 assert(ndj);
		 float vx, vy;
		 //sampleDerivative(x, y, vx, vy);
		 sampleDerivativeND((Node*) ndj, vx, vy);
		 
		 float len  = sqrt(vx*vx+vy*vy);
		 if(len) {
			 vx/=len;
			 vy/=len;
		 }
		 float xn, yn;
		 xn = graph->getAsFloat(ndj, propertyX, 0, true);
		 yn = graph->getAsFloat(ndj, propertyY, 0, true);
		 data[i][0] = xn;
		 data[i][1] = yn;
		 data[i][2] = vx;
		 data[i][3] = vy;
		 mask[i][0] = 1;
		 mask[i][1] = 1;
		 mask[i][2] = 1;
		 mask[i][3] = 1;
		 i++;
	 }
	graph->destroyIterator(it);
	 double *weight = new double[ncols];
	 for(int p=0;p<ncols;p++) weight[p] = 1.0;
	 double *error = new double[nrows];
	
	int *clusterid = new int[nrows];
	int ifound;
	
	print("Calling kcluster\n");
	int nclusters = getPropertyInt("Streamlines.Number Clusters");
	kcluster(nclusters, nrows, ncols, data, mask, weight, 0, 80, 'a', 'u', clusterid, error, &ifound);
	print("DONE\n");
	
	for(int k=0;k<nrows;k++) {
		int id = k;
		int cluster = clusterid[id];
		printf("cluster[%d] = %d\n", id, cluster);
		char str[20];
		sprintf(str, "%d", cluster); 
		graph->nodes[id]->setPropertyString("cluster", str);
	}
	controller->registerProperty("node", "cluster");
	
	delete clusterid;
	delete error;
	delete[] mask;
	delete[] data;
	delete weight;
	 }
			

//render one streamline indexed k of all streamlines
void SensitivityPlot::renderStreamline(int k, float /*minv1*/, float /*maxv1*/, float /*minv2*/, float /*maxv2*/, int linewidth/* = 1*/) {
	if(!streamlines) return;
	glLineWidth(linewidth);
	glBegin(GL_LINE_STRIP); 
	{
		for(unsigned m = 0;m<streamlines[k].size();m++) {
			float px = streamlines[k][m].x; 
			float py = streamlines[k][m].y;
			glVertex2f(px, py);
		}
	}
	glEnd();
	glLineWidth(1);
}

// Renders the streamline for hovered node k
void SensitivityPlot::renderStreamline(vector<pt> streamline, float /*minv1*/, float /*maxv1*/, float /*minv2*/, float /*maxv2*/, int linewidth/* = 1*/) {
	glLineWidth(linewidth);
	glBegin(GL_LINE_STRIP); 
	{
		for(unsigned m = 0;m<streamline.size();m++) {
			float px = streamline[m].x; 
			float py = streamline[m].y;
			//px = (px - minv1) / (maxv1-minv1);
			//py = (py - minv2) / (maxv2-minv2);
			glVertex2f(px, py);
		}
	}
	glEnd();
	glLineWidth(1);
}

void SensitivityPlot::update(string property) {
	if((property=="Grid.Logscale X") || (property=="Grid.Logscale Y")) {
		updateLines();
	}
	if(property=="Streamlines.Cluster" && getPropertyBool("Streamlines.Cluster")) {
		clustered = true;
		clusterStreamlines();
	}
	if(property=="Kmeans" && getPropertyBool("Kmeans")) {
		clusterPoints();
	}
	if(property=="Trend.Neighborhood" || property=="Trend.Length" || property=="Data.Properties" || property=="Trend.Method") {
		updateLines();
	}
	ScatterPlot::update(property);

}//endof update()

void SensitivityPlot::renderPoints() { //called by ScatterPlot::render()
	ScatterPlot::renderPoints();
	renderLines();
}

void SensitivityPlot::render() {
	string propertyX = getPropertyX();
	string propertyY = getPropertyY();
	int N = numElements();
	createLines(N);
	if(PROPERTY(getPropertyString("Data.DX"))!=curPropertyDerivX) {
		curPropertyDerivX = PROPERTY(getPropertyString("Data.DX"));
		updateLines();
	}
	if(PROPERTY(getPropertyString("Data.DY"))!=curPropertyDerivY) {
		curPropertyDerivY = PROPERTY(getPropertyString("Data.DY"));
		updateLines();
	}
	if(propertyX!=curPropertyX || propertyY!=curPropertyY) {
		updateLines();
	}
	if(hoveredNode!=previousSelected && hoveredNode!=0) {
		previousSelected = hoveredNode;
		computeStreamline((Node *)hoveredNode);
	}
	
	ScatterPlot::render();
	// Streamlines
	{
		float minv1, maxv1;
		float minv2, maxv2;
		getMinMax(propertyX, minv1, maxv1);	
		getMinMax(propertyY, minv2, maxv2);	
		if(getPropertyBool("Streamlines.Show")) {
			if(!streamlines || invalidateStreamlines) {
				computeStreamlines();
				invalidateStreamlines = false;
			}
			float lineTransparency = getPropertyFloat("Trend.Transparency");
			string colorProperty = PROPERTY(getPropertyString("Data.Color"));
			string cmap = PROPERTY(getPropertyString("Data.Colormap"));	
			ElementIterator *it = createIterator();
			int k = 0;
			for(it->begin();it->hasMoreElements();) {
				Node *node = (Node*) it->next();
				float rgb[3];
				getAsColor(node, colorProperty, cmap, rgb);
        int selected = node->getPropertyInt("node.selected");
        if(selected>0) {
					getSelectionColor(selected, rgb);
        }
				glColor4f(rgb[0], rgb[1], rgb[2], lineTransparency);
				renderStreamline(k, minv1, maxv1, minv2, maxv2,2);
				k++;
			}
			graph->destroyIterator(it);
		}
		if(hoveredNode) {
			glLineWidth(2*getPropertyInt("Trend.Line Width"));
			float rgb[3];
			rgb[0] = rgb[1] = rgb[2] = 0.0;
			getSelectionColor(selectionMarker, rgb);
			glColor4f(rgb[0],rgb[1],rgb[2],0.7);
			renderStreamline(streamline, minv1, maxv1, minv2, maxv2, 2+getPropertyInt("Trend.Line Width"));
		}
	}
	
}


View *createSensitivityPlot(string name) {
    SensitivityPlot *plot = new SensitivityPlot(name.c_str());
    plot->setup();
    plot->setLocation(0.0, 1.0);
    plot->setSize(1.0, 0.7);
    plot->setVisible(true);
    plot->addSelectionWidget();
    plot->addCloseButton();
    plot->addTransparentButton();
    return plot;
}


